home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-08-16 | 82.4 KB | 2,336 lines |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- C++ Database Library
-
-
- Version 2.3, 16th August 1995
-
-
-
-
-
-
- Package Contents
- ================
-
- The package contains the following files:
-
- SOURCE.ZIP The source files for the library.
- This file contains the following files:
-
- DATABASE.CPP Source file
- DATAEXE1.CPP Source file
- DATAEXE2.CPP Source file
- FIELD.CPP Source file
- INDCLUS.CPP Source file
- INDEX.CPP Source file
- INDEX2.CPP Source file
- RECORD.CPP Source file
- UTIL.CPP Source file
- EXOP.HPP Private header file
- DATAPRIV.HPP Private header file
- DATABASE.HPP Header file for all versions of the library
-
- DATABASE.TXT User manual for library.
- README.TXT File list and conditions
-
- DBRPT.CPP Example program
-
- FAMILYS.DBF Example database
- FAMILYS.CPP Example program
- FAMILYS.NDX Index file for example database
-
- PACKDB.CPP Example program
-
-
-
-
-
-
-
-
- Library, Conditions for use:
- ============================
-
- This library of routines is provided as shareware. You may try the
- functions defined in the library within in your own programs, however
- if you decide to incorporate them permanently then you are obliged to
- register your copy of the software. Registration costs รบ30 UK sterling
- or $40.00 (by cheque) to:
-
- Robin Abbott Compuserve ID 100023,535
- 37 Plantation Drive,
- Christchurch,
- Dorset
- ENGLAND
-
- BH23 5SG
-
- Compuserve Registration
- =======================
-
- To register this library through Compuserve GO SWREG, and follow the
- instructions there.
- Use registration number 7118. Compuserve ID 100023,535
-
- Registration brings you the next library upgrade free of charge and
- technical support.
-
- Compiled programs which incorporate the code within these libraries
- may be distributed with no further royalty.
-
- The library may be freely distributed provided that all files are
- included.
-
-
- Known Bugs, 15/8/95
- ===================
-
- I have now tested the index routines with 100,000 record databases,
- generating and changing every single record in the database.
- I am (fairly!) confident that all the significant bugs have been
- ironed out, as these tests cover index tree depths of up to 6.
- The verifyindex() routine has been made much more stringent and
- shows valid index structures for all the tests I have carried out.
-
- Bug reports
- ===========
-
- Send bug reports to the author. I will try to solve bugs and either
- return an updated software version asap, or provide a work around.
-
- Simple bugs can just be reported, e.g. the bug that field names
- could not have an underscore in them within the expression
- evaluator.
-
- More complex bugs such as all the errors that occurred in the index
- routines really need to identify the problem on a specific database.
- This may not be easy, but please try and identify the routines which
- cause the problem and send the complete database.
-
-
-
-
-
-
- Introduction
- ============
-
- This library is intended to allow Borland C++ programs to create,
- write, interrogate and manipulate databases and indexes which are in
- dBase III format. These files usually have the extension .dbf. The
- library is based on a group of objects which represent databases,
- records and fields.
-
- The library of functions will be of use in building database
- applications and report generators, both standalone, and for use with
- other database applications.
-
- Using the Library in Building Programs
- ======================================
-
- The package incorporates 9 source files, which must be included in
- the project build, or which can be built into a library. These files
- are as follows:
-
- DATABASE.CPP Source file (X)
- DATAEXE1.CPP Source file (X)
- DATAEXE2.CPP Source file (X)
- FIELD.CPP Source file (X)
- INDCLUS.CPP Source file (X)
- INDEX.CPP Source file (X)
- INDEX2.CPP Source file (X)
- RECORD.CPP Source file (X)
- UTIL.CPP Source file (X)
-
- These incorporate all the functions described in this
- document and should be compiled using the large model. Copy
- the header file database.hpp to your include directory.
-
- Programs which use the library must include the header file
- database.hpp, and must be compiled using the large model.
-
- Using the Package
- =================
-
- The package is intended to be as easy as possible to use. A database
- object should be opened with the name of the database in which the
- user is interested together with the first index file if required.
- Additional index files may be added as required. A record object (or
- many records) may now be opened on this database. The record object
- may be positioned at any record in the file and the fields in the
- record may be examined or modified. It is possible to select records
- on a variety of criteria including dBase expressions.
-
- The following example shows a program which opens the file "CLUB.DBF"
- and swaps the month and day for each record in the file. This was
- written to correct an error in importing a file in American date
- format. The file has a number of fields, the only one considered here
- is the DOB field - the date of birth which was incorrectly imported.
-
- /*
- Short Program to exchange Day & Month for every record in a
- database
- */
-
-
-
-
-
- #include <string.h>
- #include <database.hpp>
- #include <stdio.h>
- #include <stdlib.h>
-
- void main()
- {
- char ws[128]; // Convenient Workspace
-
- database db("CLUB"); // Open database, no index
-
- record rec(db); // Record open on database
-
- int fn=db.getfield("DOB")->getnumber(); // Get number of DOB field
-
- int sel=rec.select(FIRST); // Select first record
- while(!sel) // For every record in the file
- {
- int d,m,y; // day, month and year
-
- gnums(rec.getfield(fn),d,m,y); // Get date of birth to d,m,y
- sprintf(ws,"%04d%02d%02d",y,d,m); // Print in db form, swap d&m
- rec.setfield(fn,ws); // Update field
- rec.write(); // Write back to disk
-
- sel=rec.select(NEXT); // Select next record
- }
- }
-
- Indexes
- =======
-
- The package is capable of handling dBase style NDX files (indexes).
- These are attached using the database::addindex() function. Indexes
- are referred to by the file name with no path and no extension. Thus
- the index "c:\c\name.ndx" is referred to using the string "name".
-
- Indexes are usually attached when the database is opened, or when
- they have been created. New records will be included in the index
- only if the index is attached to the database. Should an out of date
- index be attached to a database then there is a risk that the records
- will be out of order, to check a database the database::verifyindex()
- function may be used to check the index.
-
- Big Files
- =========
-
- Large databases (greater than about 2000 records) need handling
- carefully to avoid speed problems. Indexes should be built on these
- databases as little as possible, being maintained by attaching them
- to the database. Selecting records should be done by using the index
- key (the record::selkey() function), which will be considerably
- faster than using any of the record::select() functions - these
- functions search the whole database from beginning to end. The
- database::verifyindex() function should also be avoided as this
- searches the entire database.
-
- Errors
- ======
-
- The database will report fatal errors (by printing the errors on
- screen) which may cause a crash such as opening a record on an
- invalid database. Some more trivial errors which may be trapped by
- the calling program will also be printed. The global variable eroff
- should be set to 1 if it is required to turn off printing of these
- non-fatal errors. Fatal errors are printed using printf for DOS
- compilations, and using MessageBox() for Windows compilations.
-
-
-
-
- Objects
- =======
-
- There are three types of object used by application programs in the
- database library.
-
- Database objects, Field Objects and Record Objects. A database object
- holds the description of a database, field objects associated with the
- database hold the definitions of the fields in the database, and
- record objects hold all the information on a record.
-
- Assignment of Objects (Use of the = Operator)
- =============================================
-
- The = operator may be used within the database library only to copy
- one record to another (within the same database), this allows the
- user to save the position and contents of a record within the
- database, the = operator is implemented to ensure that a complete
- copy of all structures and variables associated with the record is
- made. An example of the use of this is shown above in the subtotals
- program.
-
- The = operator must not be used with database objects or field
- objects, or between records on different databases. See the example
- program DBPACK below to see how a record may be copied between
- different databases.
-
- DATABASE Objects
- ================
-
- A database object is constructed with the name of the database to be
- opened, and an optional index file. The file is opened and the user
- then has the capability to determine the state of the file and the
- fields in it. Constructing a database object automatically creates a
- linked list of the fields in the database.
-
- To create a new database a database object is opened without a file
- name, and then fields of any type can be added to the database. When
- all the database fields have been written the database can be
- established by saving it with a filename after which records may be
- created within it.
-
- The database object has the following public interface:
-
- class database
- {
- public :
-
- char ers[81]; // Reports the error in an expression
-
- database(char *name,char *index=0); // Construct with file+index
- database(void); // Construct a new database
- ~database(void);
-
- int addfield(field *newfield); // Copy field to new database
- int addfield(char *nm,int l=1,int rdp=0); // Add field to new
- // database
- int addindex(char *fname); // Add index to the database
- int buildindex(char *expr,char *fn,
- void (*prog)(int,long)=0,
- int ord=100); // Build a newindex
- field *getfield(int n)
- {return(fielda[n-1]);} // Return field n, 1<=n<=nfield
- field *getfield(char *name); // Get ptr to field by name
- char *getindkey(char *name); // Get index key
- int getindtype(char *name); // Get index type, OPINT,OPSTR
- long getnrec(void) {return(nrec);} // Number of records
- int getreclen(void) {return(reclen);} // Total record length
- int getnfield(void) {return(nfield);} // Number of fields
- int getversion(int &maj,int &min); // Get library version
- int isvalid(void) {return(valid);} // Return valid flag
- int verifyindex(char *iname,int depth=1,
- void (*progress)(int test,long recnum)=0,
- int order=100); // Verify an index
- int setdateformat(dates form); // Set date format
- int subindex(char *iname); // Subtract an index
- }
-
- FIELD Objects
- =============
-
- A field object is created to enable the functions associated with the
- database to determine the format of the records. It also allows the
- user to determine the type, name and parameters of any field. The
- field type has the following public interface:
-
- class field
- {
- public:
-
- field(int number,char *name,char type,int length,int rdp,
- int recpos);
- ~field(void);
- // Accessor functions
- int getnumber(void) {return(number);} // Get no. of field in record
- char *getname(void) {strcpy(namecop,name);
- return(namecop);} // Get name of field
- char gettype(void) {return(type);} // Get type of field
- int getlen(void) {return(len);} // Get length of field
- int getrdp(void) {return(rdp);} // Get right of dp of field
- };
-
- RECORD Objects
- ==============
-
- There may be any number of record objects associated with each
- database object. Each record object holds one record in the file, and
- it is possible to load a record object with the first, last, next,
- previous, or any numbered record in the database. The record object
- has the following public interface:
-
- class record
- {
- public:
-
- record(class database &db,char *indname=0);
- ~record(void);
- void operator=(record &s); // Copy record entirely
-
- int eval(char *expr,void *result,int &rtype); // Evaluate expression
- int eval(void *result,int &rtype); // Evaluate prev. expression
- int indchk(char *exp,int &rtype); // check an index expression
- char *indname(); // Return index name
- int seldbf(long n); // Select record in dbf
- int select(long n,int type=NOTDEL,int df=0); // Select rec number
- int select(int field,int val,long n,
- int type=NOTDEL); // Select fn==val
- int select(int field,char *s,long n,
- int type=NOTDEL); // Select field==s
- int select(int field,void *s,
- int (*comp)(void *,void *),
- long n,int type=NOTDEL); // User def select
- int select(char *expr,long n,
- int type=NOTDEL); // Select when expr true
- char *getfield(int n,int tf=1); // Get field by number
- char *getfield(char *name,int tf=1); // Get field by name
- long getrecnum() {return(rn);} // Current record number
- int getdelstate() {return(delstate);} // Delete state of record
- void setdelstate(int sval); // Set delete state of record
- int selkey(char *value,int type=NOTDEL,
- int num=OPSTR); // Select by key
- int selkey(double value,int type=NOTDEL);
- int selkey(); // Find other records by key
- int setfield(char *name,char *value); // Set new value for field name
- int setfield(char *name,float value);
- int setfield(int number,char *value); // Set new value for field by #
- int setfield(int number,float value);
- int write(int type=OVER); // Write rec to dbf
- };
-
-
- Representation of Field Types
- =============================
-
- Field types in dBase are Numeric (N), Character (C), Date (D),
- Logical (L) or Memo (M). They are held internally in the follwoing
- forms:
-
- Numeric
- -------
-
- Numeric fields are held in double (8 byte) form. When values are
- returned from the database they are in ASCII form (e.g. 1.235). Thus
- the C atoi() or atof() functions should be used to convert to integer
- or floating form as required.
-
- Character
- ---------
-
- Character fields are held internally as normal C format 0 terminated
- strings.
-
- Logical
- -------
-
- Logical fields are held as a single character string (0 terminated)
- where the value is "T","t","Y", or "y" for true values and
- "F","f","N", or "n" for false values.
-
- Date
- ----
-
- Date fields are held as character strings (0 terminated) of length 8.
- These are in the form YYYYMMDD, thus 5th November 1962 is held as
- "19621105".
-
- Memo
- ----
-
- Memo fields are held as character strings which are 0 terminated,
- they may be any length up to 64000 bytes provided there is sufficient
- memory to hold them. Internally memos read from a database are held
- in a buffer which starts at 512 bytes, and is expanded when necessary
- to read a larger memo.
-
- Expression Evaluator
- ====================
-
- The expression evaluator in the database library provides a complete
- dBase compatible expression evaluation function which may be used to
- select records, may be used for calculations on records, or is used
- to create index expressions. The expression evaluator is associated
- with the database object, and there is a public string (ers in the
- database object) which holds the ASCII reperesentation of the last
- database error which occurred, see the record::eval function for an
- example.
-
- It provides the following operators which are shown in operator
- precedence (those at the top of the list are executed first), note
- that the dBase =< and => (which in dBase are equivalent to >= and <=)
- are not implemented in the library (use the <= and >= operators
- instead).
-
- () Parenthesis bracket operations and change the order of
- evaluation.
- + - Unary + and - operate on a single number.
- ** ^ Exponentiation (to the power of)
- * / Multiplication, Division
- + - Add and Subtract, Numbers, Dates or Strings
- Addition of two strings concatenates them. Subtraction on
- strings takes the spaces from the end of the first string,
- concatenates the second string to the first string, and then
- puts the spaces back at the end of the new string.
- A date may have a number added or subtracted from it which
- provides a date which is that many days in the future or
- past. A date may be subtracted from another date to give the
- number of days between the two dates.
- <
- >
- =
- <> or #
- <=
- >= These are relational operators and act on strings, dates, or
- numbers to indicate if the first item is less than (<), less
- than or equal to (<=), equal to (=), greater than (>),
- greater than or equal (>=) or not equal (<>, or #) to the
- second item. The first item must be of the same type as the
- second item.
- $ This is a string operator used in the form A$B, it returns
- true if A is identical to B, or is contained within it.
- .AND. Logical AND.
- .OR. Logical OR.
- .NOT. Logical NOT, note the lower priority than .AND. and .OR.
- which can be confusing to those used to conventional operator
- precedence.
-
- Field names be used wherever a number, string or date may be used.
-
- e.g.
-
- database db("Names");
- record rec(db);
-
- rec.select("upper(name)=\"B\"",FIRST); // Select 1st
- record where 1st character of name field is B
- rec.select("ageyears+agemonths/12<12.5",NEXT); // Select
- next record where age is less than 12.5
- rec.select("dob=ctod(\"17/4/78\")",FIRST); // Select
- 1st record where dob field is 17th April 1978.
-
-
- Expression Evaluator functions
- ==============================
-
- The following functions are available:
-
- ABS(numeric expr) Gives the absolute value of the supplied
- expression.
- ASC(string expr) Returns decimal ASCII value of the first
- character of string
- AT(char s1,char s2); If string s1 is to be found in string s2 then
- this returrns the position that it is to be
- found at (starting at 1). it returns 0 if s2
- is not to be found in s1.
- CDOW(date expr) Returns a string which names the day of the
- week supplied by date expr.
- CHR(numeric expr) Gives a string of length 1 holding the
- character which has the ASCII code supplied
- by the numeric expression.
- CMONTH Returns a string which names the month
- supplied by date expr.
- CTOD(char expr) Returns a date matching the character string
- supplied as input.
- DATE() Returns the current operating system date.
- DAY(date expr) Returns the number of the day from the
- supplied date expression.
- DOW(date expr) Returns a number representing the day of the
- week from the supplied date expression.
- DTOC(date expr) Converts the date expression to a charcter
- string in system date format.
- DTOS(date expr) Gives the dBase date string form of date expr
- (YYYYMMDD). Note this function is dBase IV,
- not dBase III.
- EXP(numeric expr) Gives the result of e to the power numeric
- expr.
- IIF(condition,expr1,expr2) If condition is true returns the result
- of evaluating expr1, otherwise it returns the
- result of evaluating expr2.
- INT(numeric expr) Gives the integer value of the supplied
- expression.
- ISALPHA(char expr) Evaluates to TRUE if the 1st character of the
- supplied expression is a letter of the
- alphabet, FALSE if not.
- ISDIGIT(char expr) Evaluates to TRUE if the 1st character of the
- supplied expression is a number, FALSE if
- not.
- ISLOWER(char expr) Evaluates to TRUE if the 1st character of the
- supplied expression is a lower case letter of
- the alphabet, FALSE if not.
- ISUPPER(char expr) Evaluates to TRUE if the 1st character of the
- supplied expression is an upper case letter
- of the alphabet, FALSE if not.
- LEFT(char expr,numeric expr)Returns the numeric expr characters from
- the left of the supplied string.
- LEN(char expr) Returns the length of the supplied character
- expression.
- LOWER(char expr) Returns a string where all letters of the
- supplied expression have been converted to
- lower case.
- LTRIM(char expr) Returns a string which is the supplied string
- with all leading spaces removed.
- MAX(expr,expr) Returns the greater of the two supplied
- numbers or dates.
- MIN(expr,expr) Returns the lesser of the two supplied
- numbers or dates.
- MOD(numeric expr,numeric expr) Gives the remainder when the first
- expression is divided by the second.
- MONTH(date expr) Returns the number of the month represented
- by the supplied date expression.
- RECCOUNT() Gives the number of records in the database.
- RECNO() Gives the record number (in the dbf file)of
- the record on which the function is
- evaluated.
- RECSIZE() Gives the length of the records in the
- database.
- REPLICATE(char expr,num expr) Returns a string made by num expr
- repeats of the supplied character
- expression.
- RIGHT(char expr, num expr) Returns the numeric expr characters from
- the left of the supplied string.
- ROUND(num expr,num dec) Gives the result of rounding off the supplied
- numeric expression to dec decimal places.
- RTRIM(char expr) (Same as TRIM).
- SOUNDEX(char expr) Gives the string of lenght 4 which results
- from running the SOUNDEX algorithm on the
- supplied character expression. Note that this
- is a dBase IV function and is not supported
- in dBase III.
- SPACE(num expr) Returns a string consisting of num expr space
- characters.
- STR(number, len, decimal) Returns the string which represents the
- number supplied. len is the total length of
- the resulting string, decimal is the number
- of decimal places.
- STUFF(str 1,start,len,str 2)
- Gives a sting which is created by taking
- str 1, removing the substring which is
- defined by start and length, and inserting
- str2 where the substring was removed.
- SUBSTR(string,start,length) Gives the string which is a substring of
- the supplied string starting at position
- start and going on for length characters. If
- length is not supplied then it returns the
- rest of the string. The string is assumed to
- start at position 1.
- SWAPDATA(string) This is a PCF function which takes the
- supplied string and takes the first ~
- character in it exchanging the information
- after the ~ for the information before it,
- and replacing the ~ by a space. Thus
- "SMITH~JOHN" becomes "JOHN SMITH". This is
- used in PCF for alphabetical sorting by
- surname but printing by proper name. This
- function is NOT dBase III compatible.
- TIME() Gives the character representation of the
- current time.
- TRIM(string expr) Gives the string which results from removing
- all trailing spaces from the supplied string.
- TYPE(expr) Gives a single character string which
- represents the result type of expr. (C, N, L,
- or D). Note that unlike dBase date
- expressions are allowed, and that memo fields
- return C, not M like they would in dBase.
- UPPER(string expr) Returns a string where all letters of the
- supplied expression have been converted to
- upper case.
- VAL(char expr) Gives the numeric value of the expression
- contained in char expr. Note that the
- expression is a simple number thus "4.5"
- returns the number 4.5, but "9/5" returns the
- number 9.
- YEAR(date expr) Gives a four digit number which is the year
- of the date expression.
-
-
- Examples
- ========
-
- PACKDB
- ======
-
- The following example shows a program which packs a database. That is
- it takes an input database and creates an identical output database
- which has deleted records removed. The program is invoked using the
- command line:
-
- PACKDB INFILE OUTFILE [INDEX]
-
- INFILE is the name of the input database OUTFILE is the output
- database which will be created. INDEX is the name of the optional
- index file which will be used to order the input file so that the
- records are written in this order to the output file.
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <database.hpp>
- #include <math.h>
-
- main(int argc, char *argv[])
- {
- char index[128]; // Holds index name if supplied
- long i;
-
- if (argc<3) // Error need at least two arguments
- {
- printf("\nUsage : packdb infile outfile [index]\n");
- printf("\n\nDuplicates infile database to outfile database");
- printf("\nremoving deleted records.\n\n");
- printf("\nindex if supplied will operate on infile to define\n");
- printf("\nthe order records are written to outfile\n\n");
- exit(1);
- }
- if (argc==4) strcpy(index,argv[3]); else *index=0; // Copy index
-
- database *dbin=new database(argv[1],index); // Open input database
- database *dbout=new database(); // Create new output
- database
-
- if (dbin->isvalid()) // Check if input database is valid
- {
- delete dbin; delete dbout;
- printf("\nUnable to open input database, error %d\n\n",dbin-
- >isvalid());
- exit(1);
- }
-
- for(i=1; i<=dbin->getnfield(); i++) // Copy all fields
- dbout->addfield(dbin->getfield(i));
-
- if (dbout->write(argv[2])) // Check database successfully written
- {
- delete dbin; delete dbout;
- printf("\nUnable to open output database\n\n");
- exit(2);
- }
-
- record *reci=new record(*dbin); // Record on old database
- record *reco=new record(*dbout); // Record on new database
-
- long recn=1;
- printf("\n");
- int rv=reci->select(FIRST); // set to 1st record (default
- undeleted)
- while(!rv)
- {
- for(i=1; i<=dbin->getnfield(); i++) // Copy all fields
- {
- if (dbin->getfield(i)->gettype()=='N') // Numeric fields
- {
- reco->setfield(i,atof(reci->getfield(i)));
- }
- else reco->setfield(i,reci->getfield(i)); // All other fields
- }
- reco->write(NEW); // Write new record
- rv=reci->select(NEXT); // Next input record
- printf("\r%ld",recn++);
- }
-
- delete reci;
- delete reco;
- delete dbin;
- delete dbout;
- printf("\n\nOperation complete\n");
- }
-
- FAMILYS
- =======
-
- The second example below shows the use of sub totals. This example is
- on a database of families which has been indexed on the field name
- SURNAME. The program finds all records which have the SURNAME field
- set to the same value in the database and works out the average of
- the field called AGE (age in years) and AGEM (age in months) within
- the family. It then returns and for every member of the family
- updates the avgage field which is the difference between the AGE and
- AGEM fields in the record and the average age worked out for the
- family.
-
- /*
- Demonstration of Subtotals,
-
- Short program to update average age for each family in familys.dbf
- The average age is a field in each record which will indicate how
- far
- away each family member is in age from the average age in that
- family
-
- Index expression in familys.ndx is :
-
- UPPER(SURNAME)
-
- Thus all family members will be grouped together
- */
-
- #include <string.h>
- #include <database.hpp>
- #include <stdio.h>
- #include <stdlib.h>
- #include <math.h>
-
- void main()
- {
- char ws[128]; // Workspace
-
- database db("familys","familys"); // Open database
- record rec(db); // Current record
- record first(db); // Used to save
- position
- int fn=db.getfield("avgage")->getnumber(); // Field no.
- avgage field
-
- int sel=rec.select(FIRST); // Select first record
- do
- {
- char surname[128]; // Holds current surname
- double avgage=0; // Holds average age
- first=rec; // Save position
- int ssel=sel; // save sel at current position
- int trec=0; // Total no. of records in patrol
- strcpy(surname,rec.getfield("surname")); // Copy in patrol
- name
-
- while(!sel && !strcmp(surname,rec.getfield("surname"))) // Until
- new name
- {
- avgage+=atof(rec.getfield("age"))+atof(rec.getfield("agem"))/12;
- trec++;
- sel=rec.select(NEXT); // On to next record
- }
- avgage/=trec;
-
- while(!ssel && !strcmp(surname,first.getfield("surname"))) //
- Round again
- {
- double age;
-
- age=atof(first.getfield("age"))+atof(first.getfield("agem"))/12;
- first.setfield(fn,int(age-avgage));
- first.write(); // Write the new record
- ssel=first.select(NEXT);
- }
- printf("\nAverage age of %s is %f",surname,avgage);
- }
- while(!sel);
- }
-
- DBRPT
- =====
-
- The following program takes a database and prints a report on it,
- printing number and length of records, and type and length of each
- file. With the -c option it will print an include file for use in a
- C++ program which contains "#define" statements for each field
- number.
-
- It takes one argument, the file name with an optional -c to print the
- the include file.
-
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <database.hpp>
-
- main(int argc, char *argv[])
- {
- char *wsp,ws[128]; // Useful work space
- int inc=0; // Flags results are to be printed in #include
- form
-
- if (argc<2) // Must have at least one parameter
- {
- printf("Usage : dbrpt filename [-c]\n");
- printf(" -c option -> #include form for header files\n");
- exit(1);
- }
- if (argc>2) if (!strcmpl(argv[2],"-c")) inc=1;
-
- strcpy(ws,argv[1]); strupr(ws);
- if (wsp=strchr(ws,'.')) *wsp=0; if (wsp=strchr(ws,'\\')) *wsp=0;
-
- // Open database
-
- database *db=new database(argv[1]);
- if (db->isvalid()) // Check if failed to open
- {
- delete db;
- printf("\nError on opening database !"); exit(1);
- }
-
- // Dump database stats.
-
- if (inc) printf("// Database %s, include file\n\n// ",ws);
- else printf("\n");
- printf("Database : %s has %ld records of length %d with %d
- fields\n",
- ws,db->getnrec(),db->getreclen(),db->getnfield());
-
- // Dump field stats for each field
-
- for(int i=1; i<=db->getnfield(); i++)
- {
- field *fld;
-
- fld=db->getfield(i);
- if (inc) printf("\n#define %-10s %3d // ",fld->getname(),i);
- else printf("\nField %3d - %-10s ",i,fld->getname());
- printf("Type %c, Length %3d, Rdp %d",
- fld->gettype(),fld->getlen(),fld->getrdp());
- }
-
- printf("\n\n");
- delete db;
- exit(0);
- }
-
- Global Utility Functions
- ========================
-
- The following are global functions available under the database
- library, they may be of use in any database application.
-
- char *getdate(time_t time);
-
- This function obtains a time in the dbf form from an ANSI C time, the
- returned string is static and is overwritten by each call to getdate.
- Thus the current date can be obtained by:
-
- #include <time.h>
- #include <database.hpp>
-
- time_t ntime;
- time(&ntime);
- char *dp=getdate(ntime);
-
- time_t gettime(char *string);
-
- This function obtains a time in the form used by the ANSI time
- functions (e.g. asctime() etc). The string should be in dBase form,
- YYYYMMDD.
-
- void gnums(char *date,int &d,int &m,int &y);
-
- This function takes the supplied date and returns the day, month and
- year functions in the variables d,m and y. See the first example
- shown above.
-
- char *ltrim(char *string);
-
- This function returns a pointer to the first character in string
- which is not a space.
-
- char *soundex(char *dest,char *src);
-
- This function runs the soundex algorithm on the string src and places
- the 4 character result in dest which is returned by the function.
-
- int strcmpdb(char *s1,char *s2,int len=-1);
-
- Compare strings using the dBase compare format. (i.e. "Bancroft"="B"
- returns TRUE, "B"="Bancroft") returns false. len is the maximum
- length to be compared, if -1 is supplied then the whole of s1 is
- examined.
-
- char *swapdata(char *dest,int schar='~');
-
- This is a PCF function which takes the supplied string and takes the
- first ~ (or whichever character is defined by schar) character in it
- exchanging the information after the ~ for the information before it,
- and replacing the ~ by a space. Thus "SMITH~JOHN" becomes "JOHN
- SMITH". This is used in PCF for alphabetical sorting by surname but
- printing by proper name. The function returns a pointer to dest.
-
- char *trim(char *string)
- char *rtrim(char *string)
-
- These functions which are identical trim all trailing spaces from the
- supplied string and return a pointer to it.
-
-
- Classes
- =======
-
- The following are the classes which make up the library , and their
- associated member functions and variables.
-
-
-
-
- database::database
-
-
- void database::database();
-
-
-
- Description : This constructor creates a database object with no
- fields and no records. The valid flag is set to -1 to
- indicate that the database has not yet been written to
- disk. Fields may be added to the database using the
- database::addfield() function. Before records may be
- written or read to or from the database then the
- database::write() function is used to establish it on
- disk.
-
-
- E.G. // Create a database : "CLUB" with three fields, name
- (character)
- // dob (date), and phone (character)
-
- #include <database.hpp>
-
- database db();
-
- db->addfield("NAME",'C',15); // Name field
- db->addfield("DOB",'D'); // DOB field
- db->addfield("PHONE",'C',11); // Phone Field
-
- db->write("CLUB");
-
-
-
-
-
-
- database::database
-
-
- void database::database(char *name,char *index=0);
-
-
-
- Description : This constructor opens an existing database which may
- have 0 or more records.
-
- The database constructor requires a file name for the
- database, and an optional file name for the dBase
- compatible index. If the index file name is supplied as
- an empty string, or if the index file cannot be opened,
- then each record follows the last in the database file.
- If an index file name is supplied then each record
- follows the last according to the index file sequence.
- After the constructor is called the database::isvalid()
- function may be called to determine if the database was
- successfully opened. Additional indexes may be added
- using the database::addindex() function
-
- The database filename is assumed to have a ".DBF"
- extension if none is supplied, and the index filename a
- ".NDX" extension. If the database has an associated memo
- file (.DBT extension) then this file is also opened.
-
- The constructor automatically creates and initialises a
- linked list of field objects which describes the fields
- of the database.
-
- Should the constructor fail then the database::isvalid()
- function will report the error, returing 0 on success.
-
-
-
-
- E.G. #include <database.hpp>
-
- database *db;
-
- db=new database("MEMBERS","NAME");
- cout << *db;
-
-
-
-
-
-
- database::Accessor Functions
-
-
- long database::getnrec(void)
-
- unsigned int database::getreclen(void);
-
- char *getindkey(name);
-
- int getindkey(name);
-
- int database::getnfield(void);
-
- field *database::getfield(int n);
-
- void database::getversion(int &major,int &minor);
-
- field *database::getfield(char *name);
-
- int database::isvalid(void);
-
-
-
- Description : These are the accessor functions for the database, and
- return various parameters about it.
-
-
- char *database::getindkey(char *name)
-
- This function returns the key on the index which has the
- supplied file name. name should not have a .NDX extension.
- The key should be copied if any modification is to be made to
- it. It returns 0 if no index of that name exists.
-
- int database::getindtype(char *name)
-
- This function returns the type of index for the index which
- has the supplied file name. name should not have a .NDX
- extension. The returned value is either OPINT or OPSTR
- depending on whether it is an integer or string index
- expression. It returns 0 if no index of the supplied name
- exists.
-
-
-
-
-
- int database::getnfield(void);
-
- This function returns the number of fields in the database.
-
- long database::getnrec(void)
-
- This function returns the number of records in the database.
-
- unsigned int database::getreclen(void);
-
- This function returns the record length of records in the
- database.
-
- void database::getversion(int &major,int &minor);
-
- This function sets major to the major number of the database
- library version and minor to the minor number. For instance
- version 2.3 will set major to 2, and minor to 3.
-
- int database::isvalid(void);
-
- This function returns a code which represents the state of
- the database object. The code takes on the following values:
-
- -2 Creating Database with a memo field, not yet
- written to disk
- -1 Creating Database, not yet written to disk
- 0 Database file opened and initialised
- successfully.
- NOFILE Database file could not be opened.
- NOINDEX Index file could not be opened.
- NOMEM Memory allocation error on database creation.
-
- E.G. #include <database.hpp>
-
-
-
- database db("CLUB","")
-
- printf("Database has :\n");
- printf("%d Records\n",db.getnrec());
- printf("%d Fields\n",db.getnfields());
-
-
-
-
-
-
- database::addfield
-
-
- int database::addfield(char *name,int type,int length=1,int
- rdp=0);
-
- int database::addfield(field *fp);
-
-
-
- Description : This function adds a field to a database which is
- being created.
-
- The first form of the function adds a defined field. name
- is the name of the field, type is the field type -
- 'C','D','M','L' , or 'N'. Length is defined for character
- and numeric fields and is the length of these fields in
- the database. The other field types are set automatically
- and the length parameter is ignored. The rdp parameter is
- only used by numeric fields to set the number of decimal
- places in the field.
-
- The second form of the function makes a new field from a
- pointer to an existing field from an existing (but
- different) database.
-
- The function returns 0 if successful, DUPFIELD if the
- name of this field exists in the database already, and
- INVFIELD if a different error occurs.
-
-
- E.G. See the database::database() function.
-
-
-
-
-
-
- database::addindex
-
-
- field *database::addindex(char *filename);
-
-
-
- Description : This function adds an index to the list of indexes
- which is maintained for each database. When an index is
- added it is available for use by records which may select
- any index, which then defines the order in which records
- are read from the database. Any index attached to a
- database is automatically updated as records are modified
- or added within the database. The file name is assumed to
- have a .NDX extension.
-
- Functions such as the record constructor which require to
- identify an index on the database use the name defined in
- the database constructor or the database::addindex()
- function. Thus the index "NAME.NDX" is referred to as
- "NAME" in functions which need to identify an index.
-
- The function returns 0 if the index is successfully
- attached to the database, and the error NOINDEX if the
- file could not be opened. It returns the error INVIND if
- there is an error in the index file or if it is already
- attached. Index file checking is limited and will not
- spot all types of index file corruption.
-
-
- E.G. #include <database.hpp>
-
- database *db;
-
- db=new database("MEMBERS","AGE");
- db.addindex("AGE");
- db.addindex("FUNC");
- db.addindex("PHDIG");
-
- record rec(db,"FUNC"); // rec uses the FUNC index
-
-
-
-
-
-
- database::buildindex
-
-
- void database::buildindex(char *expr,char *filename,
- void (*progress)(int s,long e)=0,
- int order=250);
-
-
-
- Description : This function constructs a new index on the database.
- expr is the expression that is to be used for the index
- file, it must be a valid index expression. filename is
- the name of the index file to be written, if a file
- exists it will be overwritten. Index file creation may
- take some considerable time for a large database. After a
- successful creation the index can be attached to the
- database using the database::addindex() function if it is
- desired to use it immediately.
-
- progress is the optional address of a function which can
- show the progress of the index build, and can be used for
- example, to show a bar chart of % complete.
-
- The function progress will be called every order records
- (as default order=250). The function is called with an
- integer s, which is the stage of the build, and a long e,
- which is the event.
-
- Stage runs from 1 to 3. When stage is 1, then event will
- count from 1 to the N which is the number of records.
- When stage is 2 then event will count from 1 to a maximum
- value which is calculated as N/2*log(N)/log(2), however it
- may be a lot less depending on how well sorted the records
- are in the database. When stage is 3 then event will count
- from 1 to the number of index clusters, it is usually not
- worth displaying event when stage is 3, as it is so fast.
-
- You may find that there are periods in stage 2 of the
- build (which is the sorting phase), when event is updated
- very slowly (for instance the first update may take a long
- time). This is normal.
-
- buildindex returns 0 on success, or the error EXPRERR
- if an error occurred in expression evaluation on any
- record, or the error NOINDEX if the file could not be
- opened.
-
- The filename must not be already attached to the owning
- or any other database or the program will almost
- certainly crash.
-
-
- E.G. #include <database.hpp>
-
-
- void prtbuild(int,long);
-
- database db=database("MEMBERS");
- db.buildindex("UPPER(NAME)","NAME",prtbuild);
- db.addindex("NAME");
-
- void prtbuild(int stage,long event)
- {
- printf("\rStage %d, Event %ld",stage,event);
- }
-
-
-
-
-
-
- database::getfield
-
-
- field *database::getfield(int n);
-
-
- field *database::getfield(char *name);
-
-
-
- Description : These functions return a pointer to the field class
- which is defined either by number n, or by its name. If
- the field cannot be found a zero pointer is returned.
-
-
- E.G. #include <database.hpp>
-
- field *fpt;
- database db("LIST","NAME");
-
- fpt=db.getfield("NAME");
- printf("\nName field is number : %d",fpt->getnumber());
- printf("\nName field is of length : %d",fpt->getlen());
-
-
-
-
-
-
- database::subindex
-
-
- int database::subindex(char *name);
-
-
-
- Description : This function removes an index from the list of
- indexes which is maintained for each database. The name
- is the filename which was used to add the index (from the
- database::database() or database::addindex() functions).
-
- The function returns 0 on success, or the error NOINDEX
- if the index could not be found. It will return INDINUSE
- if a record is using the index and the index will not be
- removed.
-
-
-
-
-
-
- database::setdateformat
-
-
- void database::setdateformat(dates newform);
-
-
-
- Description : This function sets the date format to be used by
- the expression evaluator. If newform is USDATE then
- the US date format is used, mm/dd/yyyy, if newform is
- UKDATE then the european date format is used :
- dd/mm/yyyy.
-
- The default is USDATE.
-
- E.G. db->setdateformat(UKDATE);
-
-
-
-
-
-
- database::write
-
-
- int database::write(char *filename);
-
-
-
- Description : This function will write a database which is newly
- created to disk. filename is the name of the database
- (without the .dbf extension) on disk. After this function
- has been executed successfully the database will be valid
- (the valid flag will be 0), and records may be written
- (and then read) on the database.
-
- It returns 0 on success, or NOFILE if the database is not
- newly created or cannot be written to disk.
-
-
- E.G. See the database::database() constructor function.
-
-
-
-
-
-
- database::verifyindex
-
-
- int database::verifyindex(char *indexname,int depth=1,
- void (*progress)(int test,long rec)=0,
- int order=100);
-
- Description : This function will run through the entire database
- using the supplied index name which should already be
- attached to the database, and check that the index matches
- the records in the database.
-
- depth controls the thoroughness of the test. If depth is 1
- (the default), then about 1000 records are checked per
- second (on a 33MHz 486), and the test checks that the
- index holds the same number of records as the database.
- If depth is 2, then the order of records is checked, if
- depth is 3, then every record in the database is found
- in the index. Depth 2 and 3 are intended mainly for
- debugging, and are too slow for normal use.
-
- progress is the optional address of a function which can
- show the progress of the index verify, and can be used for
- example, to show a bar chart of % checked.
-
- The function progress will be called every order records
- (as default order=100). The function is called with an
- integer test, which is the stage of the verify, and a long r,
- which is the record number. r runs from 1 to the selected
- depth. r runs from 1 to the number of records in the
- database for each stage.
-
- verifyindex returns:
- 0 if the index is OK.
- INCOMP if the index appears to end before all records
- have been read from the database, OORD if the
- records are out of order
- RECNOTFND if a record is not found in the index
- ERRINTREE if the index tree has an error in it
- INVIND if the index is not attached to the database,
- or there is an error in the index expression.
-
-
- E.G. database db("CLUB","NAME");
-
- if (db->verifyindex("NAME)) // On error rebuild index
- {
- db->subindex("name");
- db->buildindex("upper(name)","name");
- db->addindex("name");
- }
-
-
-
-
-
-
- field::Accessor Functions
-
-
- int field::getlen();
-
-
- char *field::getname();
-
-
- int field::getnumber();
-
-
- int field::getrdp();
-
-
- char field::gettype();
-
-
-
- Description : These functions return various information about the
- field object to which they are applied.
-
-
- int field::getlen();
-
-
- This returns the length of the field in characters.
-
-
- char *field::getname();
-
-
- This returns a pointer to the name of the field.
-
-
- int field::getnumber();
-
-
- This returns the number of the field.
-
-
- int field::getrdp();
-
-
- This returns the number of characters to the right of the
- decimal place in a numeric field.
-
-
-
-
-
- char field::gettype();
-
-
- This returns the single character field type which will be
- one of C,N,D,L, or M. These have the following meanings :
-
-
- C Character field
- N Numeric field
- D Date field
- L Logical field
- M Memo field
-
-
- E.G. #include <database.hpp>
-
-
-
- field *fpt;
- database db("DATA","");
-
- fpt=db.getfield("DATE");
- cout << "Date field is number :" << fpt->getnumber();
- cout << " and of type " << fpt->gettype();
-
-
-
- See Also :
-
-
-
-
-
-
-
- record::record
-
-
- void record::record(class database &db, char *index=0);
-
-
-
- Description : The record constructor is called with a database to
- which the record object will belong. The object may then
- hold any record from that database, and the extraction
- functions may be used to obtain any of the fields from
- the record. The selection functions are used to read a
- required record into the object.
-
- Note that the constructor reads no information into the
- record, the record contents will be invalid until the
- select functions are used.
-
- index is the index to be used with this record. If no
- indexes are in use, or if NOIND is supplied as the index
- parameter then records are read from the database in the
- order that they were written (this is fastest). If the
- index is supplied as 0 then the first index attached to
- the database is used for this record, this will be
- attached either by the database constructor, or by the
- database::addindex() function. If the index is a name
- then this is the one used.
-
- The index name is the filename of the .NDX file without
- the .NDX extension so the index file "name.ndx" is
- referred to as "name".
-
- If the index cannot be opened then no index is associated
- with the record.
-
-
- E.G. #include <database.hpp>
-
- database db("EQUIP","TITLE"); // Database constructor
- db.addindex("AGE"); // Age index is attached
- db.addindex("NAME"); // Name index is attached
-
- record rec(db); // Record uses "TITLE" index
- record rec1(db,NOIND); // Record with no index
- record rec2(db,"AGE"); // Record uses "AGE" index
-
- See Also : record::select(), database::addindex(),
- database::database()
-
-
-
-
-
-
- record::Accessor Functions
-
-
- int record::getdelstate();
-
- long record::getrecnum();
-
- char *record::indname();
-
- int record::setdelstate(int state);
-
-
-
- Description : These functions return information about the record
- object.
-
-
- int record::getdelstate();
-
-
- This function returns the delete state of the record either,
- either DEL or NOTDEL.
-
-
- long record::getrecnum();
-
-
- This function returns the record number of the record object
- in the database.
-
-
- char *record::indname();
-
-
- This function returns the index name used on this record, or
- 0 if none.
-
-
- void record::setdelstate(int state);
-
-
- This setes the delete state of the record, to deleted
- (state==DEL), or not deleted (state==NOTDEL). Note that the
- record in the dbf file is not updated until a record::write()
- function is executed.
-
-
-
-
-
-
- record::eval
-
-
- void record::eval(char *expr,void *result,int &rtype);
-
-
- void record::eval(void *result,int &rtype);
-
-
-
- Description : These functions evaluate an expression which
- is provided in ASCII (e.g. "age<12") and return a result.
- The expression is evaluated on the current record and
- values for fields in the expression are taken from the
- current record.
-
- When the function is called, the expression is tokenised
- and stored internally. If the same expression is to be
- evaluated more than once, then for the second and
- subsequent calls the second form of the expression may be
- used. This will result in faster evaluation, however the
- tokenised form of the expression is overwritten by any
- call to eval by any record, by the record::indchk()
- function, by the index building functions (which are
- called by database::addindex(), database::buildindex(),
- and by adding a record to an indexed database), and by
- the record select by expression function.
-
- The function returns 0 if there was an error in the
- evaluation in which case the owning database string ers
- may be checked for an ASCII representation of the error.
- On success the function returns 1.
-
- result points to an area of memory into which is written
- the result of the evaluation. rtype represents the type
- of the result:
-
- rtype Result Type result points to :
-
- 1 (OPINT) Numeric A double
- 2 (OPSTR) String A string
- 4 (OPDATE) Date An 8 character string in
- dBase data format
- 8 (OPLOG) Logical Single character string - F
- or T.
-
- Memo fields may be used freely in expression, however the
- expression evaluator maintains its own memory area which
- may overflow if results of string expressions are too
- long. Memo fields are stored in a buffer which may be
- modified by the evaluator, see the record::getfield
- function for a further discussion of this.
-
-
-
-
-
- E.G. // Print the age in years from the dob field
- // in the database (date of birth) and the current date.
- // Print for all those whose name begins with "A"
-
- database db("CLUB");
- record rec(db);
-
- int sel=rec.select("name\"A\"",FIRST);
-
- while(!sel) // For all records
- {
- char result[128]; // Holds result
- int dum; // Dummy variable
-
- cout << "\n" << (char *)rec.getfield("name") << "is ";
- int rv=rec.eval("int((date()-dob/365.25)",result,dum);
- if (!rv)
- {
- cout << "Couldn't calculate result because :";
- cout << db.ers;
- }
- else cout << *(double *)result << " years old";
-
- sel=rec.select("name=\"A\"",NEXT);
- }
-
-
-
-
-
-
- record::getfield
-
-
- void record::getfield(int n,int trimflag=1);
-
-
- void record::getfield(char *name,int trimflag=1);
-
-
-
- Description : These functions return a pointer to (a
- copy of) the specified field. n is the field number (the
- first field is number 1), or the field can be specified
- by name. It should be noted that it is much faster to
- obtain a field by number than by name as the latter
- requres a search of all field names in the current data
- base.
-
- Note that the string returned is not guaranteed to be
- maintained through further calls to the database library
- functions, so a copy should be made if it is required to
- maintain the field value through other functions. The
- function returns a string of spaces if the field cannot
- be found, or if a record has not been read with one of
- the select functions.
-
- If trimflag is 1 then results are trimmed, that is
- trailing spaces are removed, if it is 0 then the field is
- returned as is.
-
- The return values of the function are the internal form
- held in dbf files, thus the string will have the
- following types:
-
- FIELD TYPE FORMAT
-
- Character Zero terminated string form of field.
- Numeric ASCII representation of number, use
- atoi() or atof() to obtain
- number in integer or floating point form.
- Date String form of date : YYYYMMDD, thus
- 19/11/1965 is held as
- 19651119. Use the gettime() function to
- return
- an ANSI time in seconds which may be used
- with ANSI C time
- functions.
- Logical The return value is a single character
- which holds T,t,Y,y for TRUE and F, f, N, or n for
- FALSE.
- Memo Returns a pointer to a buffer which holds
- the memo in standard string format, see below.
-
-
-
-
-
- Memos: Memo fields are held in a buffer which is initially 512
- bytes long, but is increased in length as required as
- bigger memos are found. Not that there is only one memo
- buffer per database, and it is overwritten every time
- that a memo field is accessed from any record. Expression
- which act on memo fields may also modify this buffer.
- Thus it is important that if it is desired to keep the
- memo field intact through other database function calls
- then the buffer should be copied. The buffer (and
- therefore any memo field) has a maximum length of 64k.
- Empty memos return a null string.
-
-
- E.G. see record::select
-
-
-
-
-
-
- record::indchk
-
-
- int record::indchk(char *expr,int &rtype);
-
-
-
- Description: This function checks the supplied expression and
- returns the length of the result in an index expression
- (e.g. trim(name) has a length which is the same as the
- length of the name field). If the expression is not valid
- for an index (e.g. it's result length exceeds 100
- characters, the result is logical, or there is an error
- in the expression etc.) then the function returns 0, and
- the ers string in the database is set to the error
- report. rtype is set to the type of the expression which
- takes the same values as the rtype parameter in the
- record::eval() function.
-
-
- E.G. database db("Names","CNAME");
- record rec(db);
-
- int rt;
- int indval=rec.indchk(exprt,rt);
-
- if (!indval)
- printf("\n %s is not a valid index because
- %s",exprt,rec.db.ers);
-
-
-
-
-
-
- record::seldbf
-
-
- int record::seldbf(long n);
-
-
-
- Description: This function sets the record to record number n in
- the dbf file regardless of any index file in current use,
- or the state of the record (deleted or not). Note that
- the first record is record 1.
-
- It returns 0 on success, or CANTSEL if n is out of range.
-
- After this function the record::select() functions may
- give unpredictable results unless the
- record::select(..FIRST) function is used.
-
-
- See Also: record::select()
-
-
-
-
-
-
- record::select
-
-
- int record::select(long n,int type=NOTDEL);
-
-
- int record::select(int fieldnum,int value,long n,int
- type=NOTDEL);
-
-
- int record::select(int fieldnum,char *value,long n,int
- type=NOTDEL);
-
-
- int record::select(int fieldnum,void *value,int (*cmp)(void
- *,void *),long n,int type=NOTDEL);
-
-
- int record::select(char *expr,long n,int type=NOTDEL);
-
-
-
- Description : These functions update the record structure
- to contain the selected record from the database. The
- first function record::select(n) simply selects a record
- without regard to the contents of the record, the others
- select record by some criteria. The type parameter may be
- supplied as NOTDEL, DEL or ALL, this defines whether
- deleted, not deleted, or all records which match the
- criteria are retrieved.
-
-
-
-
- The second form selects records where the specified field
- number which is assumed to be numeric, matches the
- supplied integer value. The third form selects record
- where the specified field number matches the supplied
- string. The fourth form allows the user to supply a
- function which searches for records. The function takes
- two pointers, the first will be supplied as a string from
- the record (this is the value of the specified field),
- the second pointer is to the value supplied to the
- function.The function should return 0 if it has found a
- valid record.
-
- The final form takes a dBase expression which should
- return a logical value (e.g. "age<12") and returns all
- records for which the expression returns a True value. If
- the expression is supplied as "" then the last expression
- which was evaluated is used. (Note that any library
- function which uses the expression evaluator will modify
- the last expression). If an error occurs in evaluation or
- a non-logical value is returned, then this function
- returns CANTSEL.
-
-
-
-
-
- The functions read a record from the database according
- to the value supplied in n, and then allows the user to
- access the fields in that record using the record
- accessor functions. The function returns 0 is it was
- successful, and the error CANTSEL if it failed.
- If an index is in use then the record order specified in
- the index file is used by the select functions.
-
- If n is greater than 0 then it is the record number to be
- loaded, note that the first record is record number 1.
- The record number n is the nth record which matches the
- selection criteria, not the record number in the dbf
- file, use the record::seldbf() function is select the nth
- record in the dbf file.
-
-
-
- If n is less than 0 then it takes the values FIRST, LAST,
- NEXT or PREVIOUS. These values have the following
- meanings:
-
- FIRST The first record which matches the criteria
- is loaded.
- LAST The last record is loaded.
- NEXT The next record in sequence is loaded.
- PREVIOUS The previous record in sequence is loaded.
-
-
-
-
-
- E.G. #include <database.hpp>
-
- int compfirst(void *,void *);
-
- /*******************************/
- /* Print all names in database */
-
- void main()
- {
- database db("MEMBERS","SERIAL");
-
- record rec(db);
-
- int sel;
-
- sel=rec.select(FIRST);
- while (!sel)
- {
- printf("%s\n",rec.getfield("NAME"));
- sel=rec.select(NEXT);
- }
-
- /*********************************************/
- /* Find all members whose name begins with C */
-
- fieldnum=(db.getfield("NAME"))->number;
-
- int rv=rec.select(fieldnum,"C",compfirst,FIRST);
- while(!rv)
- {
- printf("%s\n",rec.getfield(fieldnum));
- rv=rec.select(fieldnum,"C",compfirst,NEXT);
- }
- }
-
- /***************************************************/
- /* This function compares two strings and returns */
- /* zero if the first characters are the same */
-
- int compfirst(void *name,void *value)
- {
- return(*(char *)name!=*(char *)value);
- }
-
- See Also : record::getfield()
-
-
-
-
-
-
- record::selkey
-
-
- int record::selkey(char *value,int type=NOTDEL);
-
- int record::selkey(double value,int type=NOTDEL);
-
- int record::selkey();
-
-
-
- Description : This function selects the record by the index key
- attached to the record. value is the key value to find
- and is the result of the index expression on the record
- which will be matched to the record to find it in the
- database. type is either DEL, NOTDEL or ALL and has the
- same meanings as in the record::select() functions.
-
- The final form of the function (record::selkey()) selects
- records with the same key as supplied previously.
-
- Selecting by key (when known) is much faster than using
- the record::select() functions, although the right index
- should be used.
-
- It returns with 0 on success, NOKEY if record key could
- not be found and in this case will select the record with
- the next highest key, if there is no higher key then it
- returns NOKEYHIGHER, and selects the last record by
- index.
-
- If there is no index it returns CANTSEL.
-
- The record::select() functions may be used with NEXT and
- PREVIOUS parameters after this function is used if
- required.
-
-
- E.G. // Find all records where name starts with B
-
- record rec(*db,"Name"); // Use name index
-
- int sel=rec.selkey("B");
-
- while(!sel)
- {
- printf("\n%s",rec.getfield("name"));
- sel=rec.selkey();
- }
-
-
-
-
-
-
- record::setfield
-
-
- int record::setfield(char *name,char *value);
-
- int record::setfield(char *name,double value);
-
- int record::setfield(int fieldnum,char *value);
-
- int record::setfield(int fieldnum,double value);
-
-
-
- Description : Update a field in the record. The first two forms use
- the field name to set the field, the second two forms use
- fieldnum as the number of the field. The various forms of
- the function convert the argument types to a string and
- write this to the field. Date, Logical, Memo and
- Character fields should use the char * (as the 2nd
- parameter) form of the function, Numeric fields will use
- the double form. Date and Logical field values should be
- in dBase format (the getdate() function, described in the
- Functions part of this document, is useful here).
-
- Note that the record class is updated, but the database
- is not updated until a record::write() function is
- executed.
-
- The function returns 0 if the field is set to the value
- successfully, and RECNOTSET if an error occurred.
-
- The first field is field number 1 when the numeric version
- is used.
-
-
- Memo Fields: Memo fields must provide a pointer to a buffer which
- should remain valid until the record is written. The old
- buffer in the memo file is overwritten if the new memo
- field will fit in the same space as the old one.
-
-
-
-
-
- E.G. #include <database.hpp>
-
- // Undelete all deleted records, and
- // update a field which indicates the number of
- modifications
- // to the record, this is field number 17
-
- database db("Names");
-
- int rv=rec.select(FIRST,DEL); // Select 1st deleted
- record
- while(!rv)
- {
- rec.setdelstate(NOTDEL);
- int nwrites=atoi(rec.getfield(17));
- rec.setfield(17,nwrites+1);
- rec.write();
- rv=rec.select(NEXT,DEL);
- }
-
-
-
-
-
-
- record::write
-
-
- int record::write(int type=OVER);
-
-
-
- Description : Write an updated record back to the dbf file. type is
- either OVER or NEW. If type is OVER then the record is
- written back on top of the record which was originally
- read, this form is used for updating records. If type is
- NEW then the record is added at the end of the dbf file.
-
- The function returns 0 if it was successful, and NOWRUNR
- if the application has attempted to overwrite an unread
- record, and WRFAIL if the write failed for some other
- reason.
-
- The function also updates any indexes attached to the
- current database, and it is not until this function is
- executed that these indexes are updated.
-
- The function checks to see if any other record classes
- attached to the database have the same record selected,
- if so the information is copied to these records updating
- them.
-
-
- E.G. See record::setfield()
-
-
-
-
-
-
- Appendix 1 - Format of database files
-
-
- DBase_3,_File_Format
-
-
- DBF File
-
- The DBF file holds all the records in the database together with
- global information on fields and records. It consists of 3 parts :
-
- 1) The file header which is 32 bytes long.
-
- 2) The field definitions each of which is 32 bytes long, the
- last field is followed by the byte 0DH.
-
- 3) The records each of which follows the last.
-
- File Header
-
- This is 32 bytes long, the contents are as follows :
-
- Byte 0 Bits 0-2 Version of dBase, Bit 7 flags Memo file for
- this database if set
- Bytes 1-3 Year, Month and Day (in hex) of last modification to
- database.
- Bytes 4-7 Number of records.
- Bytes 7-8 The offset of the first record from the start of the
- file.
- Bytes 10-11 The record length including the start byte.
-
- Field Definitions
-
- Each definition is 32 bytes long, the contents of each field are
- defined as follows:
-
- Bytes 0-10 Zero terminated, upper case field name, max 10
- characters.
- Byte 11 Type of the field, C, N, L, D, or M.
- Byte 16 The length of the field in characters as seen on
- screen.
- Byte 17 The number of digits right of decimal point for
- numeric fields, 0 for all others.
-
- Records
-
- Each record is stored in order of the field contents. The first byte
- of each record is either a space ' ' for an undeleted record, or a
- '*' for a deleted record. Formats for all data stored in the record
- is ASCII except for date and memo fields :
-
- Date These fields are stored in 8 digit ascii, year then
- month then day to enable simple sorting. eg 27th
- September 1991 is stored as '19910927'.
-
-
-
-
-
- Memo These fields are stored as 10 ascii digits which show
- the cluster number of the memo in the dbt file. Thus
- a value of '0000000023' is at location 23*512 in the
- memo file. This field is all 0's for no memo on this
- record.
-
-
-
-
-
- NDX, Index Files
-
- These files store the indexes for the database. They consist of a
- header cluster, and a complex hierarchical index structure which is
- based on 512 byte clusters.
-
- Header
-
- The header consists of the first cluster of the index file (512
- bytes). It contains information about the index file.
-
- Bytes 0-3 Cluster number of the first cluster of the index file
- which is at the top of the tree.
-
- Bytes 4-7 Number of clusters in the file.
-
- Byte 9 Type of result, D,C or N, Logical index expressions
- are invalid.
-
- Bytes 12-13 The size of the result of the dBase index expression
- used for this file, in bytes.
-
- Bytes 14-15 The maximum number of index records per cluster.
-
- Bytes 16-17 The result type, 0=character, 1=numeric.
-
- Bytes 18-19 The total length of the index expression plus its
- pointers. ie the value in bytes 12-13 + 8+n, where n
- is chosen to round up to the nearest 4 bytes.
-
- Byte 24+ The dBase index expression used on this file, which
- is zero terminated.
-
- Index Cluster Format
-
- Each cluster consists of a number of records. Each record starts with
- two 4 byte numbers, the block number and the record number. Following
- this is the record itself which is the result of the dBase index
- expression. The cluster starts with the number of records in it, or
- the number of records-1 for block clusters, this is also a 4 byte
- number.
-
- There are two types of cluster :
-
- A record cluster contains index records where the block number is set
- to 0, and the record number is set to the record number in the dbf
- file for this index. Following the last record is a 00000000 byte
- sequence.
-
- The second type is a block cluster which pointes in a tree like
- fashion to another block cluster, or to a record cluster. This type
- has as its records the value of the index record of the last item in
- the block to which it points. A block cluster has the record number
- set to 0, and the block number set to the cluster number of the block
- to which it refers.
-
-
-
-
-
- Memo Files
-
- Memo files are organised into 512 byte clusters. Each memo takes up
- an integral number of clusters, and the text ends in the double byte
- 1A, 1A. The first 4 bytes of the file hold the number of the next
- free block, and the rest of block 0 is free for user supplied
- comments. Block 1 is the first block available for use as a memo.
-
-